package com.afeng.web.module.app.controller;

import com.afeng.web.common.cache.JedisUtil;
import com.afeng.web.common.log.ApiLogUtils;
import com.afeng.web.common.mq.RedisMQUtil;
import com.afeng.web.common.mq.config.MqEventModel;
import com.afeng.web.common.mq.config.MqEventType;
import com.afeng.web.common.token.JwtUtil;
import com.afeng.web.common.util.QRCodeUtil;
import com.afeng.web.common.util.SignUtil;
import com.afeng.web.framework.annotation.ApiAuth;
import com.afeng.web.framework.base.ApiResult;
import com.afeng.web.framework.base.BaseApiController;
import com.afeng.web.module.app.service.DynamicDataSourceService;
import com.afeng.web.module.common.dao.BaseDao;
import com.afeng.web.module.common.dao.CommonDao;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Maps;
import com.google.common.util.concurrent.RateLimiter;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.core.task.TaskExecutor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotEmpty;
import java.awt.image.BufferedImage;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.*;

/**
 * ClassName: ApiTestController
 * Description:
 * date: 2020/4/13 22:45
 *
 * @author afeng
 * @since JDK 1.8
 */
@AllArgsConstructor
@Slf4j
@Validated
@RestController
@RequestMapping("/api/test")
public class ApiTestController extends BaseApiController {

    private final CommonDao commonDao;
    private final TaskExecutor taskExecutor;
    private final SqlSessionTemplate sqlSessionTemplate;
    private final BaseDao baseDao;
    private final TransactionTemplate transactionTemplate;
    private final DynamicDataSourceService dynamicDataSourceService;
    private final JedisUtil jedisUtil;
    private final RedisTemplate redisTemplate;
    private final StringRedisTemplate stringRedisTemplate;


    @ApiAuth()
    @GetMapping("/token")
    public ApiResult token() {
        String uid = getParameter("uid");
        String refreshToken = JwtUtil.createToken(uid);
        return success(refreshToken);
    }

    @ApiAuth()
    @GetMapping("/authToken")
    public ApiResult authToken() {
        return success(JwtUtil.getUserId());
    }

    /**
     * 数据库操作测试
     *
     * @author AFeng
     * @date 2020/11/30 11:59
     **/
    @ApiAuth()
    @GetMapping("/bd")
    public ApiResult bd(String uid) throws Exception {
        Map<String, Object> where = new HashMap<>();
        where.put("id", 1);
        Map<String, Object> Param = new HashMap<>();
        Param.put("code", 1);
        Param.put("name", "name as menuName");
        Param.put("value", "isnull(value,'无') as menuValue");
        Map<String, Object> sysOperLog = commonDao.selectOneByParam("e_upms_menu", Param, where);

        Object object = sqlSessionTemplate.selectOne("findOperLogById", 1L);

        where = new HashMap<>();
        where.put("operId", 1);
        Map<String, Object> logData = baseDao.selectOne("findOperLog", where);

        Map<String, Object> reMap = new HashMap<>();
        reMap.put("sqlSessionTemplate", object);
        reMap.put("sysOperLog", sysOperLog);
        reMap.put("logData", logData);

        return success(reMap);
    }


    /**
     * 测试简单签名验证
     * http://localhost:9090/api/test/authSimpleSign?sign=51cbb4eb55b538e3898716bfdf170b38&uid=11&a=22&b=33
     *
     * @author AFeng
     * @date 2020/11/25 15:04
     **/
    @ApiAuth
    @GetMapping("/authSimpleSign")
    public ApiResult authSimpleSign() {
        return success();
    }

    /**
     * 创建简单签名
     * http://localhost:9090/api/test/simpleSign?uid=11&a=22&b=33
     *
     * @author AFeng
     * @date 2020/11/25 15:04
     **/
    @ApiAuth()
    @GetMapping("/simpleSign")
    public ApiResult simpleSign() throws Exception {
        return success(SignUtil.createSimpleSign(request, "free2020"));
    }

    /**
     * 创建简单登录token
     * http://localhost:9090/api/test/simpleLoginToken?uid=11
     *
     * @author AFeng
     * @date 2020/11/25 14:40
     **/
    @ApiAuth()
    @GetMapping("/simpleLoginToken")
    public ApiResult simpleLoginToken(String uid) {
        if (StringUtils.isEmpty(uid)) {
            return fail();
        }
        return success(JwtUtil.createTokenByAES(uid));
    }

    /**
     * 简单登录验证
     * http://localhost:9090/api/test/authSimpleLogin?token=8c7d16193d0425645abf1777fe038f66e7809d2e88dcc9bdd4a4c5889b29d944
     *
     * @author AFeng
     * @date 2020/11/25 14:26
     **/
    @ApiAuth()
    @GetMapping("/authSimpleLogin")
    public ApiResult authSimpleLogin(@ApiAuth String tokenToUid) {
        return success(tokenToUid == null ? "token失效" : tokenToUid);
    }

    /**
     * 测试参数验证
     *
     * @author AFeng
     * @date 2020/11/25 14:27
     **/
    @ApiAuth()
    @GetMapping("testData")
    public ApiResult testData(@NotEmpty(message = "用户UID不能为空") String uid, @NotEmpty(message = "test不能为空") String test) {
        return success(uid);
    }

    /**
     * 测试多线程
     *
     * @author AFeng
     * @date 2020/11/25 14:27
     **/
    @ApiAuth()
    @GetMapping("testTaskExecutor")
    public ApiResult testTaskExecutor() {
        FutureTask<Object> task = new FutureTask<Object>(new Callable<Object>() {
            @Override
            public Object call() throws Exception {
                try {
                    ApiLogUtils.debugPrint("打印");
                } catch (Throwable e) {
                }
                return "ok";
            }
        });
        taskExecutor.execute(task);    //为提升访问速率, 日志记录采用异步的方式进行.

        return success();
    }


    /**
     * 返回二维码图片流
     *
     * @author AFeng
     * @date 2020/12/28 17:54
     **/
    @RequestMapping("/appGetQrCode")
    public void appGetQrCode(HttpServletResponse response) {
        try {
            String data = getParameter("data");//转码链接
            if (org.apache.commons.lang3.StringUtils.isNotEmpty(data)) {
                data = URLDecoder.decode(data, "UTF-8");
                BufferedImage image = QRCodeUtil.createImage(data);
                ImageIO.write(image, "png", response.getOutputStream());

                response.setHeader("Access-Control-Allow-Origin", "*");
                response.setContentType("image/png");
                response.flushBuffer();
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }


    /**
     * 测试多数据源
     *
     * @author AFeng
     * @date 2021/1/28 16:50
     **/
    @GetMapping("testDynamicDataSource")
    public String testDynamicDataSource() {
        Integer freedom_userCount = dynamicDataSourceService.selectFreedomDataSource();
        Integer afengboot_userCount = dynamicDataSourceService.selectAfengbootDataSource();
        log.info("freedom中的用户表总量为{}", freedom_userCount);
        log.info("afengboot中的评价表总量为{}", afengboot_userCount);
        log.info("selectFreedomMapper中的用户表总量为{}", dynamicDataSourceService.selectFreedomMapper());
        log.info("selectAfengbootMapper中的评价表总量为{}", dynamicDataSourceService.selectAfengbootMapper());
        log.info("selectFreedomDao中的用户表总量为{}", dynamicDataSourceService.selectFreedomDao());
        log.info("selectAfengbootDao中的评价表总量为{}", dynamicDataSourceService.selectAfengbootDao());
        return "success";
    }


    /**
     * 测试自定义事务控制
     *
     * @author afeng
     * @date 2021/2/12 13:31
     */
    @GetMapping("/testTransactionTemplate")
    public ApiResult testTransactionTemplate() {
//        Boolean isSuccess = transactionTemplate.execute(new TransactionCallback<Boolean>() {
//            @Override
//            public Boolean doInTransaction(TransactionStatus action) {
//                try {
//                    Map<String, Object> where = Maps.newHashMap();
//                    where.put("id", "2");
//                    Map<String, Object> params = Maps.newHashMap();
//                    params.put("mpopenid", "123");
//                    int result = commonDao.update("app_user", params, where);
//                    commonDao.insert("app_user", params);
//                    if (result > 0) {
//                        action.setRollbackOnly();
//                        return false;
//                    } else {
//                        return true;
//                    }
//                } catch (Exception e) {
//                    action.setRollbackOnly();
//                }
//                return false;
//            }
//        });
        Boolean isSuccess = transactionTemplate.execute(action -> {
            try {
                Map<String, Object> where = Maps.newHashMap();
                where.put("id", "2");
                Map<String, Object> params = Maps.newHashMap();
                params.put("mpopenid", "123");
                int result = commonDao.update("app_user", params, where);
                commonDao.insert("app_user", params);
                if (result > 0) {
                    //回滚
                    action.setRollbackOnly();
                    return false;
                } else {
                    return true;
                }
            } catch (Exception e) {
                //回滚
                action.setRollbackOnly();
            }
            return false;
        });
        return success(isSuccess);
    }

    @GetMapping("/testTransactionTemplate2")
    public ApiResult testTransactionTemplate2() {
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {

            @Override
            protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                try {
                    Map<String, Object> where = Maps.newHashMap();
                    where.put("id", "2");
                    Map<String, Object> params = Maps.newHashMap();
                    params.put("mpopenid", "1234");
                    commonDao.insert("app_user", params);
                    int result = commonDao.update("app_user", params, where);
                    if (result > 0) {
                        //回滚
                        transactionStatus.setRollbackOnly();
                    }
                } catch (Exception e) {
                    //回滚
                    transactionStatus.setRollbackOnly();
                }
            }
        });
        return success();
    }


    /**
     * 测试MyBatis拦截器
     *
     * @author afeng
     * @date 2021/2/12 14:33
     */
    @GetMapping("/testMyBatisInterceptor")
    public ApiResult testMyBatisInterceptor() {
        Map<String, Object> where = Maps.newHashMap();
        Map<String, Object> params = Maps.newHashMap();
        params.put("unionid", "1234");
        commonDao.update("app_user", params, where);
        return success();
    }


    /**
     * 测试redis消息队列操作
     */
    @GetMapping("/redisMqProducerSend")
    public ApiResult redisMqProducerSend() {
        // 后续生成的私信内容需要这些额外信息
        Map<String, Object> articleInfo = new HashMap<>();
        articleInfo.put("uid", getParameter("uid"));
        articleInfo.put("articleName", "测试");
        articleInfo.put("commentContent", "它支持添加元素");

//        redisTemplate.convertAndSend("mq:test", JSON.toJSONString(new MqEventModel()
//                .setEventType(MqEventType.COMMENT)
//                .setExtraInfo(articleInfo)));
//        stringRedisTemplate.convertAndSend("mq:test02", "a22");
//        RedisMQUtil.getInstance().seedEvent("mq:test01", "b11");
        RedisMQUtil.getInstance().seedEvent("mq:test01", "b11");
        RedisMQUtil.getInstance().seedEvent("mq:test02", JSON.toJSONString(new MqEventModel()
                .setEventType(MqEventType.COMMENT)
                .setExtraInfo(articleInfo)));

        return success();
    }


    // qps设置为5，代表一秒钟只允许处理五个并发请求
    private static RateLimiter rateLimiter = RateLimiter.create(5);

    /**
     * 测试限流器
     */
    @GetMapping("/testRateLimiter")
    public ApiResult testRateLimiter() throws Exception {
        // 1.限流处理 限流正常要放在网关 客户端从桶中获取对应的令牌，为什么返回double结果，这个结果表示 从桶中拿到令牌等待时间.
        // 2. 如果获取不到令牌，就会一直等待.设置服务降级处理（相当于配置在规定时间内如果没有获取到令牌的话，直接走服务降级。）
        // double acquire = rateLimiter.acquire();
        //
        // System.out.println("从桶中获取令牌等待的时间:" + acquire);
        // 如果在500毫秒内如果没有获取到令牌的话，则直接走服务降级处理
        boolean tryAcquire = rateLimiter.tryAcquire(500, TimeUnit.MILLISECONDS);

        ExecutorService executorService = Executors.newFixedThreadPool(5);
        int nTasks = 10;
        CountDownLatch countDownLatch = new CountDownLatch(nTasks);
        long start = System.currentTimeMillis();
        for (int i = 0; i < nTasks; i++) {
            final int j = i;
            executorService.submit(() -> {
                if (tryAcquire) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                    }
                    System.out.println(Thread.currentThread().getName() + "抢到了~ " + j + " done");
                }
                countDownLatch.countDown();
            });
        }
        executorService.shutdown();
        countDownLatch.await();
        long end = System.currentTimeMillis();
        System.out.println("10 jobs gets done by 5 threads concurrently in " + (end - start) + " milliseconds");
        return success();
    }

}
